package com.zx.idc.backend.gui.api.management.accesslog.logquery.service.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.zx.idc.backend.gui.api.commom.entity.CommandModel;
import com.zx.idc.backend.gui.api.commom.entity.UploadModel;
import com.zx.idc.backend.gui.api.commom.template.NIdcFtpTemplate;
import com.zx.idc.backend.gui.api.management.accesslog.logquery.service.LogQueryService;
import com.zx.idc.common.attributes.FTPReportDataTypeEnum;
import com.zx.idc.common.attributes.NetCommunicationFileTypeEnum;
import com.zx.idc.common.attributes.SchemaFileNameEnum;
import com.zx.idc.common.configuration.AppProtocolConfiguration;
import com.zx.idc.common.configuration.ContentTypeRepo;
import com.zx.idc.ds.base.oper.service.IBaseOperCompanyService;
import com.zx.idc.ds.common.constant.ReportStatusConstant;
import com.zx.idc.ds.httplog.entity.AccessLog;
import com.zx.idc.ds.httplog.entity.wapper.AccessLogEntity;
import com.zx.idc.ds.httplog.repository.AccessLogRepository;
import com.zx.idc.ds.ins.entity.InsAccesslogQuery;
import com.zx.idc.ds.ins.service.IInsAccesslogQueryService;
import com.zx.idc.xml.entity.logquery.LogQuery;
import com.zx.idc.xml.entity.logquery.commandinfo.CommandInfo;
import com.zx.idc.xml.entity.logqueryresult.LogQueryResult;
import com.zx.idc.xml.entity.logqueryresult.log.Log;
import com.zx.idc.xml.entity.logqueryresult.result.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.zx.idc.common.attributes.LogQueryAttributes.COLLECTION_NAME_PREFIX;
import static com.zx.idc.common.attributes.LogQueryAttributes.LOG_QUERY_REPORT_NUM;
import static com.zx.idc.common.lang.StringUtil.append;

/**
 * Created by hasee on 2019/2/15.
 * 访问日志查询接口 11.5 11.6
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class LogQueryServiceImpl implements LogQueryService {
    private final static Logger LOG = LoggerFactory.getLogger(LogQueryServiceImpl.class);

    @Autowired
    private AccessLogRepository accessLogRepository;

    @Autowired
    private ContentTypeRepo contentTypeRepo;

    @Autowired
    private NIdcFtpTemplate nIdcFtpTemplate;

    @Autowired
    private IInsAccesslogQueryService iInsAccesslogQueryService;

    @Autowired
    private IBaseOperCompanyService iBaseOperCompanyService;

    @Override
    public void logQuery(LogQuery logQuery, CommandModel commandModel) {
        try {
            LOG.debug("Access log query interface 11.5 11.6 execute start");
            //保存指令 上报状态为 上报中
            LOG.debug("1.1 Save the instruction and the reporting status is reporting start");
            iInsAccesslogQueryService.insert(logQuery2InsAccessLogQuery(logQuery));
            LOG.debug("1.1 Save the instruction and the reporting status is reporting done");

            //设置结束时间
            CommandInfo commandInfo = logQuery.getCommandInfo();
            commandInfo.setEndTime(getEndTime(commandInfo));
            long houseId = commandInfo.getHouseId();

            //条件集合
            AccessLogEntity queryEntityList = logQuery2MongoDBAccessLog(logQuery);

            //获取集合名称
            List<String> collectionNames = getCollectionNames(houseId, commandInfo);

            //查询数据
            List<AccessLog> accessLogs = accessLogRepository.selectList(collectionNames, queryEntityList);

            //按每1000个一组分割
            List<List<AccessLog>> parts = Lists.partition(accessLogs, LOG_QUERY_REPORT_NUM);
            String idcId = iBaseOperCompanyService.getIdcId();
            LOG.debug("1.2 upload accessLog file start");
            parts.forEach(list -> {
                //构造logQueryResult
                LogQueryResult logQueryResult = new LogQueryResult();
                Result result = logQueryResult.getResult().setLogAmount(list.size())
                    .setEndFlag(1);
                //最后一个
                if (list.size() < LOG_QUERY_REPORT_NUM) {
                    result.setEndFlag(0);
                }
                List<Log> logs = list.stream().map(accessLog ->
                    new Log().setUrl(accessLog.getUrl())
                        .setDestIp(accessLog.getDestIp())
                        .setDestPort(accessLog.getDestPort())
                        .setHouseId(houseId)
                        .setSrcIp(accessLog.getSrcIp())
                        .setSrcPort(accessLog.getSrcPort())
                        .setAccessTime(LocalDateTime.ofInstant(accessLog.getAccessTime().toInstant(), ZoneId.systemDefault()))
                        .setLogId(accessLog.getId())
                ).collect(Collectors.toList());
                logQueryResult.setLog(logs)
                    .setTimeStamp(LocalDateTime.now())
                    .setCommandId(logQuery.getCommandId())
                    .setIdcId(idcId);
                //调用ftp模板
                nIdcFtpTemplate.uploadXMLFile(
                    new UploadModel<>(
                        logQueryResult,
                        SchemaFileNameEnum.ACCESS_LOG_QUERY_RETURN_REPORT,
                        FTPReportDataTypeEnum.FTP_ACCESS_LOG_QUERY_RECORD,
                        NetCommunicationFileTypeEnum.LOG_QUERY
                    )
                );
            });
            LOG.debug("1.2 upload accessLog file done");
            LOG.debug("Access log query interface 11.5 11.6 execute done");
        } catch (Exception e) {
            LOG.error("访问日志查询指令上报异常", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 转换成数据库实体
     *
     * @param logQuery
     * @return
     */
    private InsAccesslogQuery logQuery2InsAccessLogQuery(LogQuery logQuery) {

        CommandInfo commandInfo = logQuery.getCommandInfo();
        InsAccesslogQuery insAccesslogQuery = new InsAccesslogQuery()
            .setId(String.valueOf(logQuery.getCommandId()))
            .setDstPort((int) commandInfo.getDstPort())
            .setSrcPort((int) commandInfo.getSrcPort())
            .setHouseId(commandInfo.getHouseId())
            .setRawPacket(JSON.toJSONString(logQuery))
            .setIdcId(logQuery.getIdcId())
            .setStartTime(commandInfo.getStartTime())
            .setEndTime(commandInfo.getEndTime())
            .setProtocolType((int) commandInfo.getProtocolType())
            .setUrl(commandInfo.getUrl())
            .setTimeStamp(logQuery.getTimeStamp())
            .setReportStatus(ReportStatusConstant.REPORTING);
        Optional.ofNullable(commandInfo.getDestIp()).ifPresent(destIp -> insAccesslogQuery.setDestIp(append(destIp.getStartIp(), "-", destIp.getEndIp())));
        Optional.ofNullable(commandInfo.getSrcIp()).ifPresent(srcIp -> insAccesslogQuery.setSrcIp(append(srcIp.getStartIp(), "-", srcIp.getEndIp())));
        return insAccesslogQuery;
    }

    /**
     * 获取需要查询的集合名称列表
     * 集合名称组合规则 accesslog_{机房id}_{yyyyMMdd}
     *
     * @param houseId     机房ID
     * @param commandInfo
     * @return
     */
    private List<String> getCollectionNames(long houseId, CommandInfo commandInfo) {
        LocalDateTime startTime = commandInfo.getStartTime();
        Duration between = Duration.between(startTime, commandInfo.getEndTime());
        //获取相差的天数
        long l = between.toDays();
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMdd");
        ArrayList<String> collectionNames = new ArrayList<>();
        if (l == 0) {
            String dayTime = df.format(startTime);
            String collectionName = getCollectionName(houseId, dayTime);
            collectionNames.add(collectionName);
        } else {
            for (int i = 0; i < l; i++) {
                String dayTime = df.format(startTime.plusDays(i));
                collectionNames.add(
                    getCollectionName(houseId, dayTime)
                );
            }
        }
        return collectionNames;
    }

    /**
     * 获取集合名称
     *
     * @param houseId 机房id
     * @param dayTime 时间
     * @return
     */
    private String getCollectionName(long houseId, String dayTime) {
        return new StringBuilder().append(COLLECTION_NAME_PREFIX)
            .append(houseId)
            .append("_")
            //查询的日期
            .append(dayTime)
            .toString();
    }

    /**
     * 业务对象转为mongoDB实体
     *
     * @param logQuery
     * @return
     */
    private AccessLogEntity logQuery2MongoDBAccessLog(LogQuery logQuery) {
        CommandInfo commandInfo = logQuery.getCommandInfo();
        AccessLogEntity accessLog = new AccessLogEntity()
            .setStartTime(commandInfo.getStartTime())
            .setEndTime(commandInfo.getEndTime())
            .setDestPort((int) commandInfo.getDstPort())
            .setSrcPort((int) commandInfo.getSrcPort())
            .setUrl(commandInfo.getUrl())
            .setTransProtocol((int) commandInfo.getProtocolType());
        //设置两个IP
        Optional.ofNullable(commandInfo.getSrcIp()).ifPresent(accessLog::setSrcIpEntity);
        Optional.ofNullable(commandInfo.getDestIp()).ifPresent(accessLog::setDestIpEntity);
        return accessLog;
    }

    ;

    /**
     * 获取结束时间
     * 规则：设定了结束时间则返回，否则结束时间=开始时间加两个小时
     *
     * @param commandInfo
     * @return
     */
    private LocalDateTime getEndTime(CommandInfo commandInfo) {
        LocalDateTime startTime = commandInfo.getStartTime();
        return Optional.ofNullable(commandInfo.getEndTime()).orElse(
            startTime.plusHours(2)
        );
    }

    ;

}
